/**
 * 
 */
package org.gpp.proj1.map;

import com.jme.bounding.BoundingBox;
import com.jme.math.Vector3f;
import com.jme.renderer.ColorRGBA;
import com.jme.renderer.RenderQueue;
import com.jme.renderer.Renderer;
import com.jme.scene.Node;
import com.jme.scene.shape.Quad;
import com.jme.scene.state.AlphaState;
import com.jme.scene.state.LightState;
import com.jme.system.DisplaySystem;

/**
 * A node that carries a map and some util information about the map,
 * like heights and modifiers.
 * @author Gustavo de Farias
 */
public class MapNode extends Node {
	
	// the render side of a map block
	public static final int TILE_SIZE = 24;
	// the render height of a map block
	public static final int TILE_HEIGHT = TILE_SIZE / 6;
	// the gap between two selection tiles
	private static final float INTERTILE_GAP = 1.0f;
	// the offset of selection tile above block surface
	private static final float HEIGHT_OFFSET = 0.05f;
	// blocks with this modifier will not be selectable
	private static final byte INACCESSIBLE_BLOCK_MODIFIER = -1;

	/**
	 * 
	 */
	private static final long serialVersionUID = -5758727453699890058L;
	
	// original information used to build the map
	private byte[][] heights;
	private byte[][] modifiers;
	private int width;
	private int height;
	
	/**
	 * Constructs a Map Node.
	 * @param name Name for the node.
	 * @param heights
	 * @param modifiers
	 */
	public MapNode(String name, byte[][] heights, byte[][] modifiers, int width, int height) {
		super(name);
		
		this.heights = heights;
		this.modifiers = modifiers;
		
		this.width = width;
		this.height = height;
	}
	
	/**
	 * @param name
	 */
	public MapNode(String name) {
		super(name);
	}
	
	/**
	 * @return
	 */
	public byte[][] getHeights() {
		return heights;
	}
	
	/**
	 * @return
	 */
	public byte[][] getModifiers() {
		return modifiers;
	}
	
	public void activateCursorTile(int l, int c, ColorRGBA color) {
		Quad tile = new Quad( "Cursor " + l + ", " + c, MapNode.TILE_SIZE, MapNode.TILE_SIZE);
		
		tile.setLocalTranslation(getTileTranslation(l, c));
		tile.setModelBound(new BoundingBox());
		tile.updateModelBound();
		tile.setSolidColor(color);
		
		
		// make tile translucent
		AlphaState as = DisplaySystem.getDisplaySystem().getRenderer().createAlphaState();
		as.setBlendEnabled(true);
	    as.setEnabled(true);
		
	    tile.setRenderState(as);
		tile.updateRenderState();
		tile.updateGeometricState( 0, true );
		
		this.attachChild( tile );
		this.updateGeometricState(0, true);
	}
	
	public void deactivateCursorTile(int l, int c) {
		
		this.detachChildNamed( "Cursor " + l + ", " + c );
		this.updateGeometricState(0, true);
	}
	
	/**
	 * Creates a selection region around a block.
	 * @param l block line
	 * @param c block column
	 * @param range The range of selection region
	 * @param color The color or region tiles
	 */
	public void activateSelectionTiles(int l, int c, int range, ColorRGBA color) {
		
		// selection region node
		Node selectionTiles = new Node(getSelectionTilesName(l, c));
		selectionTiles.setLightCombineMode(LightState.OFF);
		
		// add tiles
		for (int line = l - range; line <= l + range; line++) {
			int aux = Math.abs(line - l); 
			for (int col = c - range + aux; col <= c + range - aux; col++) {
				// only accessible blocks will receive a tile
				if (validPosition(line, col)) {
					// create tile
					Quad tile = new Quad( "Q" + line + ", " + col, MapNode.TILE_SIZE - MapNode.INTERTILE_GAP, MapNode.TILE_SIZE - MapNode.INTERTILE_GAP);
					// tile positioning
					tile.setLocalTranslation(getTileTranslation(line, col));
					tile.setModelBound(new BoundingBox());
					tile.updateModelBound();
					tile.setSolidColor(color);					
					
					tile.updateRenderState();
					tile.updateGeometricState( 0, true );
					// add to node
					selectionTiles.attachChild(tile);
				}				
			}
		}
		
		// make region translucent
		AlphaState as = DisplaySystem.getDisplaySystem().getRenderer().createAlphaState();
		as.setDstFunction( AlphaState.DB_SRC_ALPHA );
		as.setBlendEnabled(true);
	    as.setEnabled(true);
	    
		selectionTiles.setRenderState(as);
		selectionTiles.updateRenderState();

		// add region to the map
		this.attachChild(selectionTiles);
		this.updateGeometricState(0, true);
	}
	
	/**
	 * Translate a tile to the correct position according to
	 * map information.
	 * @param line
	 * @param col
	 * @return
	 */
	private Vector3f getTileTranslation(int line, int col) {
		// TODO Quando a orienta��o dos eixos for corrigida
		// � preciso trocar o Y com o Z.
		return new Vector3f(col * MapNode.TILE_SIZE,
							this.height - line * MapNode.TILE_SIZE,
							this.heights[line][col] * MapNode.TILE_HEIGHT + MapNode.HEIGHT_OFFSET);
	}

	/**
	 * Determines if a given block of map is selectable.
	 * @param line
	 * @param col
	 * @return
	 */
	private boolean validPosition(int line, int col) {
		return line < heights.length && line >= 0 && // tests bounds
			   col < heights[0].length && col >=0 && // tests bounds
			   // test if block is accessible
			   this.modifiers[line][col] != MapNode.INACCESSIBLE_BLOCK_MODIFIER;
	}

	/**
	 * Deactivate a selection region
	 * @param l line 
	 * @param c column
	 */
	public void deactivateSelectionTiles(int l, int c) {
		
		// remove the region node from map
		this.detachChildNamed(getSelectionTilesName(l, c));
		this.updateGeometricState(0, true);
	}
	
	/**
	 * Gets the name of a selection tile.
	 * @param l line
	 * @param c column
	 * @return the name of a selection tile.
	 */
	private String getSelectionTilesName(int l, int c) {
		return l + ", " + c;
	}
}